package cn.edu.buaa.neo;

import cn.edu.buaa.neo.codegraphql.core.ioc.Environment;
import cn.edu.buaa.neo.codegraphql.dsl.IQueryable;
import cn.edu.buaa.neo.codegraphql.dsl.lib.engine.QueryEngine;
import cn.edu.buaa.neo.codegraphql.dsl.lib.format.JsonResultFormatter;
import cn.edu.buaa.neo.codegraphql.helper.LogProvider;
import com.google.common.reflect.ClassPath;
import org.apache.commons.cli.*;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Main function to run queries in command line.
 * Usage: {@code Main --language=<language> [--style=<style>] [--config=<config_path>]}
 * By default, will use `config.json` under current directory.
 */
public class Main {
    public static void main(String[] args) throws Exception {
        var cmd = parseArgs(args);
        String language = cmd.getOptionValue("language");
        String style = cmd.getOptionValue("style", "console");
        String config = cmd.getOptionValue("config", "./config.json");
        boolean clearCache = cmd.hasOption("no-cache");

        String packageName = switch (language.toLowerCase()) {
            case "python" -> "query.python";
            default -> throw new Exception("Language " + args[0] + " not supported yet");
        };

        var queries = findAllQueries(packageName);
        var engine = QueryEngine.getInstance(config).withStyle(style);

        if (clearCache) {
            LogProvider.DEFAULT.info("Clearing cache...");
            Environment.getInstance().getServiceProvider().build();
            Environment.getInstance().getDbContext().getCacheProxy().clear();
        }

        if (style.equalsIgnoreCase("json-compact")) {
            engine.withFormatter(new JsonResultFormatter().minified());
        } else if (style.equalsIgnoreCase("json")) {
            engine.withFormatter(new JsonResultFormatter());
        }

        for (var query : queries) {
            engine.execute((IQueryable) query.getConstructor().newInstance());
        }
        engine.close();
    }

    private static CommandLine parseArgs(String[] args) {
        Options options = new Options();

        Option language = new Option("l", "language", true, "language");
        language.setRequired(true);
        options.addOption(language);

        Option style = new Option("s", "style", true, "output style");
        style.setRequired(false);
        options.addOption(style);

        Option config = new Option("c", "config", true, "configuration file path");
        config.setRequired(false);
        options.addOption(config);

        Option cache = new Option(null, "no-cache", false, "clear cache");
        cache.setRequired(false);
        options.addOption(cache);

        CommandLineParser parser = new DefaultParser();
        HelpFormatter formatter = new HelpFormatter();
        try {
            return parser.parse(options, args);
        } catch (ParseException e) {
            System.out.println(e.getMessage());
            formatter.printHelp("CodeGraphQL Engine", options);
            System.exit(1);
        }

        return null;
    }

    public static List<Class<?>> findAllQueries(String packageName) throws IOException {
        return ClassPath.from(ClassLoader.getSystemClassLoader())
                .getAllClasses()
                .stream()
                .filter(clazz -> {
                    try {
                        return clazz.getPackageName().contains(packageName) &&
                                IQueryable.class.isAssignableFrom(Class.forName(clazz.getName()));
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                })
                .map(ClassPath.ClassInfo::load)
                .collect(Collectors.toList());
    }
}
